package com.zaxxer.sansorm;

import java.lang.reflect.Method;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.function.Supplier;

/**
 * Lightweight optional logger. If SLF4J is present at runtime, logs via SLF4J; otherwise no-ops.
 *
 * Detection is done once and cached per logger Class.
 */
public final class LoggerFactory {
    private static final boolean SLF4J_AVAILABLE = isClassPresent("org.slf4j.LoggerFactory");
    private static final ConcurrentMap<Class<?>, LoggerFactory> CLASS_TO_LOGGER = new ConcurrentHashMap<>();

    private final Delegate delegate;

    private LoggerFactory(Delegate delegate) {
        this.delegate = delegate;
    }

    public static LoggerFactory getLogger(Class<?> clazz) {
        return CLASS_TO_LOGGER.computeIfAbsent(clazz, c -> {
            Delegate created = null;
            if (SLF4J_AVAILABLE) {
                created = Slf4jDelegate.tryCreate(c);
            }
            return new LoggerFactory(created != null ? created : NoopDelegate.INSTANCE);
        });
    }

    public void trace(Supplier<String> messageSupplier) { delegate.trace(messageSupplier); }
    public void debug(Supplier<String> messageSupplier) { delegate.debug(messageSupplier); }
    public void info(Supplier<String> messageSupplier)  { delegate.info(messageSupplier); }
    public void warn(Supplier<String> messageSupplier)  { delegate.warn(messageSupplier); }
    public void error(Supplier<String> messageSupplier) { delegate.error(messageSupplier); }
    public void error(Supplier<String> messageSupplier, Throwable t) { delegate.error(messageSupplier, t); }

    private interface Delegate {
        void trace(Supplier<String> msg);
        void debug(Supplier<String> msg);
        void info(Supplier<String> msg);
        void warn(Supplier<String> msg);
        void error(Supplier<String> msg);
        void error(Supplier<String> msg, Throwable t);
    }

    private static final class NoopDelegate implements Delegate {
        static final NoopDelegate INSTANCE = new NoopDelegate();
        public void trace(Supplier<String> msg) {}
        public void debug(Supplier<String> msg) {}
        public void info(Supplier<String> msg) {}
        public void warn(Supplier<String> msg) {}
        public void error(Supplier<String> msg) {}
        public void error(Supplier<String> msg, Throwable t) {}
    }

    private static final class Slf4jDelegate implements Delegate {
        private final Object logger;
        private final Method isTraceEnabled;
        private final Method isDebugEnabled;
        private final Method isInfoEnabled;
        private final Method isWarnEnabled;
        private final Method isErrorEnabled;
        private final Method trace;
        private final Method debug;
        private final Method info;
        private final Method warn;
        private final Method error1;
        private final Method error2;

        private Slf4jDelegate(Object logger,
                              Method isTraceEnabled, Method isDebugEnabled, Method isInfoEnabled, Method isWarnEnabled, Method isErrorEnabled,
                              Method trace, Method debug, Method info, Method warn, Method error1, Method error2) {
            this.logger = logger;
            this.isTraceEnabled = isTraceEnabled;
            this.isDebugEnabled = isDebugEnabled;
            this.isInfoEnabled = isInfoEnabled;
            this.isWarnEnabled = isWarnEnabled;
            this.isErrorEnabled = isErrorEnabled;
            this.trace = trace;
            this.debug = debug;
            this.info = info;
            this.warn = warn;
            this.error1 = error1;
            this.error2 = error2;
        }

        static Delegate tryCreate(Class<?> clazz) {
            try {
                // 尝试多个类加载器来创建 SLF4J logger
                ClassLoader[] loaders = {
                    LoggerFactory.class.getClassLoader(),
                    Thread.currentThread().getContextClassLoader(),
                    ClassLoader.getSystemClassLoader()
                };
                
                for (ClassLoader loader : loaders) {
                    if (loader != null) {
                        try {
                            Class<?> loggerFactoryCls = Class.forName("org.slf4j.LoggerFactory", false, loader);
                            Method getLogger = loggerFactoryCls.getMethod("getLogger", Class.class);
                            Object logger = getLogger.invoke(null, clazz);

                            Class<?> loggerCls = Class.forName("org.slf4j.Logger", false, loader);
                            Method isTraceEnabled = loggerCls.getMethod("isTraceEnabled");
                            Method isDebugEnabled = loggerCls.getMethod("isDebugEnabled");
                            Method isInfoEnabled  = loggerCls.getMethod("isInfoEnabled");
                            Method isWarnEnabled  = loggerCls.getMethod("isWarnEnabled");
                            Method isErrorEnabled = loggerCls.getMethod("isErrorEnabled");

                            Method trace = loggerCls.getMethod("trace", String.class);
                            Method debug = loggerCls.getMethod("debug", String.class);
                            Method info  = loggerCls.getMethod("info", String.class);
                            Method warn  = loggerCls.getMethod("warn", String.class);
                            Method error1 = loggerCls.getMethod("error", String.class);
                            Method error2 = loggerCls.getMethod("error", String.class, Throwable.class);

                            return new Slf4jDelegate(logger,
                                    isTraceEnabled, isDebugEnabled, isInfoEnabled, isWarnEnabled, isErrorEnabled,
                                    trace, debug, info, warn, error1, error2);
                        } catch (Throwable ignore) {
                            // 继续尝试下一个类加载器
                        }
                    }
                }
                return null;
            } catch (Throwable ignore) {
                return null;
            }
        }

        private static boolean enabled(Object logger, Method isEnabled) {
            try { return (boolean) isEnabled.invoke(logger); } catch (Throwable t) { return false; }
        }

        private static void call(Object logger, Method m, String msg) {
            try { m.invoke(logger, msg); } catch (Throwable ignore) {}
        }

        public void trace(Supplier<String> msg) { if (enabled(logger, isTraceEnabled)) call(logger, trace, msg.get()); }
        public void debug(Supplier<String> msg) { if (enabled(logger, isDebugEnabled)) call(logger, debug, msg.get()); }
        public void info(Supplier<String> msg)  { if (enabled(logger, isInfoEnabled))  call(logger, info,  msg.get()); }
        public void warn(Supplier<String> msg)  { if (enabled(logger, isWarnEnabled))  call(logger, warn,  msg.get()); }
        public void error(Supplier<String> msg) { if (enabled(logger, isErrorEnabled)) call(logger, error1, msg.get()); }
        public void error(Supplier<String> msg, Throwable t) {
            if (enabled(logger, isErrorEnabled)) {
                try { error2.invoke(logger, msg.get(), t); } catch (Throwable ignore) {}
            }
        }
    }

    private static boolean isClassPresent(String className) {
        try {
            // 尝试多个类加载器来检测 SLF4J
            ClassLoader[] loaders = {
                LoggerFactory.class.getClassLoader(),
                Thread.currentThread().getContextClassLoader(),
                ClassLoader.getSystemClassLoader()
            };
            
            for (ClassLoader loader : loaders) {
                if (loader != null) {
                    try {
                        Class.forName(className, false, loader);
                        return true;
                    } catch (Throwable ignore) {
                        // 继续尝试下一个类加载器
                    }
                }
            }
            return false;
        } catch (Throwable e) {
            return false;
        }
    }
}


